package de.unibi.techfak.msp.eatThis.manager;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.util.Date;


import de.unibi.techfak.msp.eatThis.manager.exception.EntityException;
import de.unibi.techfak.msp.eatThis.manager.exception.EntityNotFoundException;


/**
 * Die Superklasse für alle Manager. Ein Manager ist genau einem Bean 
 * zugeordnet. Sind relative Bezüge zu anderen Beans nötig, muss der Manager 
 * Felder der jeweiligen Untermanager deklarieren.
 * @author thoerman
 * @param <E> Der Beantype der vom Manager verwaltet wird.
 *
 */
abstract class GenericManager<E> {
    
//    Einstellung für Export
    private String connectionString = 
            "jdbc:mysql://localhost:3311/team5";
        
        private String connectionUser = "team5";
        private String connectionPaswd = "ema7nop1";
    private String lastErrorMsg;
    
    
    
    /**
     * Versucht das übergebene bean zu Aktualisieren. Konnte ein Bean nicht 
     * gefunden werden wird eine Exception geworfen. Die update methode sollte
     * normalerweise public sein. 
     * 
     * @param bean das zu aktualisierende Bean
     * @throws EntityException Falls das Bean nicht gefunden wurde.
     * @return Die Id des Bean falls das Aktualisieren funktioniert hat.
     */
    abstract Integer update(E bean) throws EntityException;

    /**
     * Erzeugt einen Datenbankeintrag für das angegebene Bean. Die Methode 
     * schlägt fehl falls die id nicht null war (der Datenbankeintrag existiert)
     * @param bean Das zu erstellende Bean.
     * @return Id des Bean falls das Bean erzeugt werden konnte sonst null.
     */
    abstract Integer create(E bean);
    
    /**
     * Versucht ein Bean aus der Datenbank zu laden. Konnte ein Bean nicht 
     * gefunden werden wird eine Exception geworfen.
     * 
     * @param beanId die id (pk) des angeforderten Bean.
     * @throws EntityException Falls das Bean nicht gefunden wurde.
     * @return Das angeforderte Bean.
     */
    abstract E get(Integer beanId) throws EntityException;

    /**
     * Versucht ein Bean aus der Datenbank zu löschen.
     * 
     * @param beanId die id (pk) des zu löschenden Bean.
     * @throws EntityException Falls das Bean nicht gefunden wurde.
     */
    abstract void delete(Integer beanId) throws EntityException;

    
    /**
     * Lädt den Datenbanktreiber und baut eine Verbindung zur Datenbank auf die
     * zurück gegeben wird. Die Verbindung sollte geschlossen werden wenn sie
     * nicht länger gebraucht wird.
     * @return Die erzeugte Verbindung
     */
    protected final Connection openConnection() {
        try {
            // Treiber der Datenbank nachladen.
            Class.forName("com.mysql.jdbc.Driver");
            
            Connection con = DriverManager.getConnection(connectionString, 
                    connectionUser, connectionPaswd);
            return con;
          } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return null;
          } catch (SQLException e) {
            e.printStackTrace();
            return null;
          }
    }
    
    /**
     * Kann genutzt werden um den generierten pk eines create Statments
     *  abzuholen.
     * @param ps Das <b>CREATE (!)</b> Statement das ausgeführt werden soll
     * @return Den pk falls ps erfolgreich.
     * @throws SQLException etwas furchtbares ist geschehen.
     */
    protected final Integer executeCreatePs(final PreparedStatement ps) 
        throws SQLException {
        // Das PS ausführen.
        ps.executeUpdate();
        // Resultset abholen.
        ResultSet rs = ps.getGeneratedKeys();
        while (rs.next()) {
            return rs.getInt(1);
        }
        return null;
    }
    
    /**
     * @param ps Das Statment
     * @return count Auswertung
     * @throws SQLException Falls das statement nicht ausgeführt werden konnte
     */
    protected final Integer executeCountPs(final PreparedStatement ps)
        throws SQLException {
        // Statement ausfuehren
        ResultSet rs = ps.executeQuery();

        if (rs.next()) { // hole das Zaehlergebnis
            return rs.getInt("rowcount");
        } else { // sonst keine Eintraege vorhanden
            return 0;
        }
    }

    /**
     * @param ex Nachricht aus der Exception erzeugen.
     * @since m301
     */
    protected final void setLastErrorMsg(final Exception ex) {
        this.lastErrorMsg = 
            String.format("Fehler [%s]: %s. %s",
                DateFormat.getDateTimeInstance().format(new Date()),
                ex.getMessage(),
                (ex.getCause() == null 
                    ? "" : " \n Ursache: " 
                        + ex.getCause().getMessage()));
    }

    /**
     * @return Gibt die letzte Fehlermeldung aus die Aufgezeichnet wurde.
     * @since m301
     */
    public final String getLastErrorMsg() {
        return lastErrorMsg;
    }

    /**
     * Ueberprueft, ob das uebergebene Argument null ist.
     * @param o ist eine ID oder eine Bean.
     * @throws EntityNotFoundException falls keine Argumente uebergeben wurden
     */
    protected void checkArguments(final Object o) 
    throws EntityNotFoundException {
        checkArguments(o, null);
    }
    
    /**
     * Ueberprueft, ob das uebergebene Argument null ist.
     * @param o ist eine ID oder eine Bean.
     * @param objDescription Beschreibt den Parameter
     * @throws EntityNotFoundException falls keine Argumente uebergeben wurden
     */
    protected void checkArguments(final Object o, 
            final String objDescription) throws EntityNotFoundException {
        if (o == null) {
            throw new EntityNotFoundException(
            "Argument " 
                    + (objDescription == null ? "" : objDescription) 
                    +  " darf nicht null sein!");
        }
    }
    

    /**
     * Schließt eine Verbindung.
     * @param con ist die übergebene Verbindung.
     */
    protected void closeConnection(final Connection con) {
        try {
            con.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
}
